﻿#region Copyright (C) 2008, 2009 StreamTv

/* 
 *      Copyright (C) 2008, 2009 StreamTv
 *      http://code.google.com/p/mpstreamtv/
 *
 *  This Program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *   
 *  This Program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *  GNU General Public License for more details.
 *   
 *  You should have received a copy of the GNU General Public License
 *  along with GNU Make; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
 *  http://www.gnu.org/copyleft/gpl.html
 *
 */

#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TvControl;
using System.IO;
using TimeShifting.Media.Encoders;
using StreamTv2;

namespace TimeShifting
{
    public class Manager : IWebService, IDisposable
    {
        #region Variables
        // Private Variables
        private IController controller;
        private Dictionary<String, TvControl.User> channelsTimeshifting;    // ChannelId, Associated user with channel.
        private Dictionary<String, String> users;                           // ChannelId, ClientId.

        private Media.MediaFactory mediaLoader;
        // Protected Variables
        // Public Variables
        #endregion
        #region Constructors/Destructors
        public Manager()
        {
        }
        #endregion
        #region Public Methods
        
        public string Name
        {
            get { return "TimeShift"; }
        }

        public int BufferSize(string clientId)
        {
            try
            {
                User client = channelsTimeshifting[users[clientId]];
                if (controller.CurrentChannel(ref client).IsRadio)
                {
                    // Audio only.
                    return 2560; //2.5Kb
                }
                else
                {
                    // Audio and video.
                    return 524288; // 512Kb
                }
            }
#if DEBUG
            catch (Exception e)
            {
                throw new Exception(String.Format("Problem getting the buffer size for clientId: {0}", clientId), e);
            }
#else
            catch (Exception)
            {
                TvLibrary.Log.Log.Error("StreamTv: Plugin: TimeShift - Problem getting the buffer size for ClientID: {0}", clientId);
                return 0x1000;
            }
#endif
        }

        public void Initialize(TvControl.IController tvController)
        {
            controller = tvController;

            mediaLoader = new TimeShifting.Media.MediaFactory();

            channelsTimeshifting = new Dictionary<string, User>();
            users = new Dictionary<string, string>();
        }

        public void ClientDisconnected(string clientId)
        {
            String channelId = null;

            // Remove the current user from the list.
            if (users.Keys.Contains(clientId))
            {
                channelId = users[clientId];

                users.Remove(clientId);
            }

            // Check if any other users are still watching the channel.
            if (users.Values.Contains(channelId))
            {
                return;
            }

            // No one is watching so stop TimeShifting.
            try
            {
                // No one else is watching the channel so stop the tuner.
                User user = channelsTimeshifting[channelId];

                controller.StopTimeShifting(ref user);

                channelsTimeshifting.Remove(channelId);
            }
            catch (Exception)
            {
            }
        }

        public void HeartBeat(string clientId)
        {
            try
            {
                controller.HeartBeat(channelsTimeshifting[users[clientId]]);
            }
#if DEBUG
            catch (Exception e)
            {
                throw new Exception(String.Format("Problem with heatbeat for client: {0}", clientId), e);
            }
#else
            catch (Exception)
            {
                TvLibrary.Log.Log.Error("StreamTv: Plugin: TimeShift - Problem with heatbeat for client: {0}", clientId);
            }
#endif
        }

        public Boolean HeatbeatRequired(string clientId)
        {
            return true;
        }

        public ErrorCode Process(string clientId, StreamTv2.RequestDetails details, out Stream media, out string mime)
        {
            try
            {
                // Get the important details together.
                int channelId = 0;

                // Get the channel to tune to.
                if (details.Query.AllKeys.Contains("ChannelId"))
                {
                    if (!int.TryParse(details.Query["ChannelId"], out channelId))
                    {
                        media = null;
                        mime = null;

                        return ErrorCode.NotFound;
                    }
                }
                else
                {
                    media = null;
                    mime = null;

                    return ErrorCode.NotFound;
                }

                TranscodeOptions options = new TranscodeOptions(details);

                //************ Start TimeShifting.

                // Check if someone is already watching the channel were interested in.
                if (!channelsTimeshifting.ContainsKey(channelId.ToString()))
                {
                    // No one is watching this channel, need to tune in.
                    VirtualCard vCard;
                    User user;

                    user = new User(clientId, false);

                    if (controller.StartTimeShifting(ref user, channelId, out vCard) != TvResult.Succeeded)
                    {
                        // Give it 1 second (the client might of disconnected and the TvServer needs a moment to close off the last session).
                        System.Threading.Thread.Sleep(2000);
                        
                        // Try one more time.
                        if (controller.StartTimeShifting(ref user, channelId, out vCard) != TvResult.Succeeded)
                        {
                            // Something went wrong.
                            media = null;
                            mime = null;

                            return ErrorCode.InternalError;
                        }
                    }

                    channelsTimeshifting.Add(channelId.ToString(), vCard.User);
                }

                // Register the user with the channel.
                users.Add(clientId, channelId.ToString());

                //************ Get Stream.

                // Get the client details.
                User client = channelsTimeshifting[users[clientId]];

                // Load the media.
                int tries = 3;

                do
                {
                    if (controller.IsTimeShifting(ref client))
                    {
                        String fileName = controller.TimeShiftFileName(ref client);

                        media = mediaLoader.Load(fileName, options);

                        // Check if the buffer loaded fine.
                        if (media == null)
                        {
                            // Something went wrong.
                            media = null;
                            mime = null;

                            return ErrorCode.InternalError;
                        }

                        // Set the mime.
                        mime = MimeHelper.Mime.Get(media);

#if DEBUG
                        mime = "video/m2ts";
#endif

                        // Return the final media.
                        return ErrorCode.Successful;
                    }

                    System.Threading.Thread.Sleep(10);
                } while (--tries != 0);

                // Failed to load media.
                media = null;
                mime = null;

                return ErrorCode.InternalError;
            }
#if DEBUG
            catch (Exception e)
            {
                System.Diagnostics.Debug.Write(String.Format(@"TimeShift: {0}\r\n{1}", e.Message, e.InnerException));
                throw new Exception("TimeShift Error", e);
            }
#else
            catch (Exception e)
            {
                TvLibrary.Log.Log.Error("StreamTv: Plugin: TimeShift - Error processing client. Exception: {0}", e.Message);

                media = null;
                mime = null;

                return ErrorCode.InternalError;
            }
#endif
        }
        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            // Close all open channels.
            List<User> clients = channelsTimeshifting.Values.ToList();

            for (int i = 0; i < clients.Count; i++)
            {
                User client = clients[i];
                controller.StopTimeShifting(ref client);
            }

            channelsTimeshifting.Clear();
            channelsTimeshifting = null;

            users.Clear();
            users = null;

            controller = null;

            mediaLoader = null;
        }
        #endregion
    }
}
